
package makai.util;

import java.awt.Color;

/**
 * A collection of color based utility methods for images and colors.
 * 
 * TODO: Finish JavaDoc
 * 
 * @author Krechevskoy
 * @version 0.8
 */
public class Toolkit {
    
    /**
     * Converts from HSB to RGB color types
     * 
     * @param hue        Represented by a float value in the range [0.0, 1.0]
     * @param saturation Represented by a float value in the range [0.0, 1.0]
     * @param brightness Represented by a float value in the range [0.0, 1.0]
     * 
     * @return An array of float RGB values in the range [0.0, 1.0] 
     */
    public static float[] HSBtoRGB(float hue, float saturation, float brightness) {
        hue -= (int)hue;
        
        int h = (int)(hue * 6);
        float f = hue * 6 - h;
        float p = brightness * (1 - saturation);
        float q = brightness * (1 - f * saturation);
        float t = brightness * (1 - (1 - f) * saturation);

        switch (h) {
          case 0: return new float[]{brightness, t, p};
          case 1: return new float[]{q, brightness, p};
          case 2: return new float[]{p, brightness, t};
          case 3: return new float[]{p, q, brightness};
          case 4: return new float[]{t, p, brightness};
          case 5: return new float[]{brightness, p, q};
          default: 
              throw new RuntimeException(
                      "Something went wrong when converting from HSB to RGB. "
                      + "Input was " + hue + ", " + saturation + ", " + brightness);
        }
    }
    
    /**
     * Converts from HSB to RGB color types
     * 
     * @param hsb        HSB represented by a float value array in the range [0.0, 1.0]
     * 
     * @return An array of float RGB values in the range [0.0, 1.0] 
     */
    public static float[] HSBtoRGB(float[] hsb){
        return HSBtoRGB(hsb[0], hsb[1], hsb[2]);
    }
    
    /**
     * Converts from RGB to HSB color types
     * 
     * @param r Represented by a integer value in the range [0, 255]
     * @param g Represented by a integer value in the range [0, 255]
     * @param b Represented by a integer value in the range [0, 255]
     * 
     * @return An array of float HSB values in the range [0.0, 1.0] 
     */
    public static float[] RGBtoHSB(int r, int g, int b){
        return Color.RGBtoHSB(r, g, b, null);
    }
    
    /**
     * Converts from RGB to HSB color types
     * 
     * @param rgb RGB represented by a integer value array in the range [0, 255]
     * 
     * @return An array of float HSB values in the range [0.0, 1.0] 
     */
    public static float[] RGBtoHSB(int[] rgb){
        return RGBtoHSB(rgb[0], rgb[1], rgb[2]);
    }
    
    /**
     * Converts from RGB to HSB color types
     * 
     * @param r Represented by a float value in the range [0.0, 1.0]
     * @param g Represented by a float value in the range [0.0, 1.0]
     * @param b Represented by a float value in the range [0.0, 1.0]
     * 
     * @return An array of float HSB values in the range [0.0, 1.0] 
     */
    public static float[] RGBtoHSB(float r, float g, float b){
        return RGBtoHSB((int)(r*255), (int)(g*255), (int)(b*255));
    }
    
    /**
     * Converts from RGB to HSB color types
     * 
     * @param rgb RGB represented by a float value array in the range [0.0, 1.0]
     * 
     * @return An array of float HSB values in the range [0.0, 1.0] 
     */
    public static float[] RGBtoHSB(float[] rgb){
        return RGBtoHSB(rgb[0], rgb[1], rgb[2]);
    }
    
    /**
     * Converts a color to a color code hex string
     * 
     * @param r Represented by a float value in the range [0.0, 1.0]
     * @param g Represented by a float value in the range [0.0, 1.0]
     * @param b Represented by a float value in the range [0.0, 1.0]
     * 
     * @return A hex string representation of the color
     */
    public static String RGBtoString(float r, float g, float b) {
        return RGBtoString((int)(r*255), (int)(g*255), (int)(b*255));
    }
    
    /**
     * Converts a color to a color code hex string
     * 
     * @param r Represented by a integer value in the range [0, 255]
     * @param g Represented by a integer value in the range [0, 255]
     * @param b Represented by a integer value in the range [0, 255]
     * 
     * @return A hex string representation of the color
     */
    public static String RGBtoString(int r, int g, int b) {
        String rs = Integer.toHexString(r);
        String gs = Integer.toHexString(g);
        String bs = Integer.toHexString(b);
        return "#"+rs + gs + bs;
    }
    
    /**
     * Converts a color to a color code hex string
     * 
     * @param rgb Represented by a integer value in the range [0, 16,777,215]
     * 
     * @return A hex string representation of the color
     */
    public static String RGBtoString(int rgb) {
        return RGBtoString(rgb & 0xFF0000, rgb&0xFF00, rgb&0xFF);
    }
    
    /**
     * Shifts the HSB values of a Color
     * 
     * @param c The starting color
     * 
     * @param h The amount to shift the hue
     * @param s The amount to shift the saturation from [-1.0, 1.0]
     * @param b The amount to shift the brightness from [-1.0, 1.0]
     * 
     * @return The appropriately shifted color
     */
    public static Color shiftHSB(Color c, float h, float s, float b){
        float[] rgb = c.getColorComponents(null);
        float[] hsb = RGBtoHSB(rgb);
        
        hsb[0] += h;
        hsb[1] += s;
        hsb[2] += b;
        
        hsb[1] = (hsb[1]<0.0f)?0.0f:( (hsb[1]>1.0f)?1.0f:hsb[1] );
        hsb[2] = (hsb[2]<0.0f)?0.0f:( (hsb[2]>1.0f)?1.0f:hsb[2] );
        
        rgb = HSBtoRGB(hsb);
        
        return new Color(rgb[0], rgb[1], rgb[2], c.getAlpha()/255.0f);
    }
    
    /**
     * Shifts the HSB values of a Color
     * 
     * @param c The starting color
     * 
     * @param h The amount to shift the hue
     * @param s The amount to shift the saturation from [-1.0, 1.0]
     * @param b The amount to shift the brightness from [-1.0, 1.0]
     * @param wrap If excessive values should be wrapped instead of clipped
     * 
     * @return The appropriately shifted color
     */
    public static Color shiftHSB(Color c, float h, float s, float b, boolean wrap){
        float[] rgb = c.getColorComponents(null);
        float[] hsb = RGBtoHSB(rgb);
        
        hsb[0] += h;
        hsb[1] += s;
        hsb[2] += b;
        
        if(wrap){
            hsb[1] %= 1.0f;
            hsb[2] %= 1.0f;
        } else {
            hsb[1] = (hsb[1]<0.0f)?0.0f:( (hsb[1]>1.0f)?1.0f:hsb[1] );
            hsb[2] = (hsb[2]<0.0f)?0.0f:( (hsb[2]>1.0f)?1.0f:hsb[2] );
        }
        
        rgb = HSBtoRGB(hsb);
        
        return new Color(rgb[0], rgb[1], rgb[2], c.getAlpha()/255.0f);
    }
    
    /**
     * @return A color with the given HSB values
     */
    public static Color getHSBColor(float h, float s, float b){
        float rgb[] = HSBtoRGB(h, s, b);
        
        return new Color(rgb[0], rgb[1], rgb[2]);
    }
    
    /**
     * @return A color with the given HSB values
     */
    public static Color getHSBColor(float[] hsb){
        float rgb[] = HSBtoRGB(hsb);
        
        return new Color(rgb[0], rgb[1], rgb[2]);
    }
    
    public static float[] getRGB(Color c){
        return c.getRGBComponents(null);
    }
    
    public static float[] getHSB(Color c){
        return RGBtoHSB(getRGB(c));
    }
    
    public static Color invertHSB(Color c){
        return shiftHSB(c, 0.5f, 0.5f, 0.5f, true);
    }
    
    public static Color invertRGB(Color c){
        float rgb[] = c.getComponents(null);
        
        return new Color(1.0f-rgb[0], 1.0f-rgb[1], 1.0f-rgb[2]);
    }
    
    
}
